package net.sk.china.city.talk.service.impl.wechat;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import net.sk.china.city.talk.constants.WeChatConstants;
import net.sk.china.city.talk.model.wechat.WechatExceptions;
import net.sk.china.city.talk.response.wechat.*;
import net.sk.china.city.talk.service.system.SystemConfigService;
import net.sk.china.city.talk.service.wechat.WechatExceptionsService;
import net.sk.china.city.talk.service.wechat.WechatNewService;
import net.sk.china.common.exception.TalkException;
import net.sk.china.common.utils.*;
import net.sk.china.common.vo.WxRefundVo;
import org.springframework.stereotype.Service;

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * WechatNewServiceImpl 接口实现
 * +----------------------------------------------------------------------
 * // | Talk To [ 聊来改善地区金融服务环境，助力企业发展 ]
 * // +----------------------------------------------------------------------
 * // | Copyright (c) 2018-2024 聊来 All rights reserved.
 * // +----------------------------------------------------------------------
 * // | Licensed ( <a href="http://www.apache.org/licenses/LICENSE-2.0">apache</a> )
 * // +----------------------------------------------------------------------
 * // | @Author: 聊来 <18970881148@qq.com>
 * // +----------------------------------------------------------------------
 * // | DateTime: 2024/2/21 9:00
 * // +----------------------------------------------------------------------
 */

@Service
public class WechatNewServiceImpl implements WechatNewService {

    private final RestTemplateUtil restTemplateUtil;

    private final RedisUtil redisUtil;

    private final WechatExceptionsService wechatExceptionsService;

    private final SystemConfigService systemConfigService;

    public WechatNewServiceImpl(RestTemplateUtil restTemplateUtil, RedisUtil redisUtil, WechatExceptionsService wechatExceptionsService, SystemConfigService systemConfigService) {
        this.restTemplateUtil = restTemplateUtil;
        this.redisUtil = redisUtil;
        this.wechatExceptionsService = wechatExceptionsService;
        this.systemConfigService = systemConfigService;
    }

    /**
     * 获取公众号模板消息列表
     * @return {@code List<PublicTemplateResponse>}
     */
    @Override
    public List<PublicTemplateResponse> getPublicTemplateList() {
        String accessToken = getPublicAccessToken();
        String url = StrUtil.format(WeChatConstants.WECHAT_PUBLIC_GET_ALL_PRIVATE_TEMPLATE_URL, accessToken);
        JSONObject jsonObject = restTemplateUtil.getData(url);
        if (ObjectUtil.isNull(jsonObject)) {
            throw new TalkException("微信平台接口异常，没任何数据返回！");
        }
        if (jsonObject.containsKey(WeChatConstants.WECHAT_ERR_CODE) && !jsonObject.getString(WeChatConstants.WECHAT_ERR_CODE).equals(WeChatConstants.NUM_ZERO)) {
            if (jsonObject.containsKey(WeChatConstants.WECHAT_ERR_MSG)) {
                // 保存到微信异常表
                wxExceptionDispose(jsonObject, StrUtil.format("获取公众号模板消息列表异常"));
                throw new TalkException("微信接口调用失败：" + jsonObject.getString(WeChatConstants.WECHAT_ERR_CODE) + jsonObject.getString(WeChatConstants.WECHAT_ERR_MSG));
            }
        }
        JSONArray templateList = jsonObject.getJSONArray("template_list");

        return templateList.toJavaList(PublicTemplateResponse.class);
    }

    /**
     * 删除微信公众号模板消息
     * @param templateId 模版ID
     * @return {@code boolean}
     */
    @Override
    public boolean delPublicTemplate(String templateId) {
        String accessToken = getPublicAccessToken();
        String url = StrUtil.format(WeChatConstants.WECHAT_PUBLIC_DEL_PRIVATE_TEMPLATE_URL, accessToken);
        HashMap<String, String> map = new HashMap<>(2);
        map.put("template_id", templateId);
        JSONObject jsonData = JSONObject.parseObject(JSONObject.toJSONString(map));
        String result = restTemplateUtil.postJsonData(url, jsonData);
        JSONObject data = JSONObject.parseObject(result);
        if (ObjectUtil.isNull(data)) {
            throw new TalkException("微信平台接口异常，没任何数据返回！");
        }
        if (data.containsKey(WeChatConstants.WECHAT_ERR_CODE) && !data.getString(WeChatConstants.WECHAT_ERR_CODE).equals(WeChatConstants.NUM_ZERO)) {
            if (data.containsKey(WeChatConstants.WECHAT_ERR_MSG)) {
                wxExceptionDispose(data, "删除微信公众号模板消息异常");
                throw new TalkException("微信接口调用失败：" + data.getString(WeChatConstants.WECHAT_ERR_CODE) + data.getString(WeChatConstants.WECHAT_ERR_MSG));
            }
        }
        return true;
    }

    /**
     * 添加公众号模板消息
     * @param templateIdShort 模板库中模板编号
     * @return {@code String}
     */
    @Override
    public String addPublicTemplate(String templateIdShort) {
        String accessToken = getPublicAccessToken();
        String url = StrUtil.format(WeChatConstants.WECHAT_PUBLIC_API_ADD_TEMPLATE_URL, accessToken);
        HashMap<String, String> map = new HashMap<>(2);
        map.put("template_id_short", templateIdShort);
        JSONObject jsonData = JSONObject.parseObject(JSONObject.toJSONString(map));
        String result = restTemplateUtil.postJsonData(url, jsonData);
        JSONObject data = JSONObject.parseObject(result);
        if (ObjectUtil.isNull(data)) {
            throw new TalkException("微信平台接口异常，没任何数据返回！");
        }
        if (data.containsKey(WeChatConstants.WECHAT_ERR_CODE) && !data.getString(WeChatConstants.WECHAT_ERR_CODE).equals(WeChatConstants.NUM_ZERO)) {
            if (data.containsKey(WeChatConstants.WECHAT_ERR_MSG)) {
                wxExceptionDispose(data, "添加公众号模板消息异常");
                throw new TalkException("微信接口调用失败：" + data.getString(WeChatConstants.WECHAT_ERR_CODE) + data.getString(WeChatConstants.WECHAT_ERR_MSG));
            }
        }
        return data.getString("template_id");
    }

    /**
     * 获取当前帐号下的模板列表(订阅消息)
     * @return {@code List<RoutineTemplateResponse>}
     */
    @Override
    public List<RoutineTemplateResponse> getRoutineTemplateList() {
        String accessToken = getMiniAccessToken();
        String url = StrUtil.format(WeChatConstants.WECHAT_MINI_GET_ALL_PRIVATE_TEMPLATE_URL, accessToken);
        JSONObject jsonObject = restTemplateUtil.getData(url);
        if (ObjectUtil.isNull(jsonObject)) {
            throw new TalkException("微信平台接口异常，没任何数据返回！");
        }
        if (jsonObject.containsKey(WeChatConstants.WECHAT_ERR_CODE) && !jsonObject.getString(WeChatConstants.WECHAT_ERR_CODE).equals(WeChatConstants.NUM_ZERO)) {
            if (jsonObject.containsKey(WeChatConstants.WECHAT_ERR_MSG)) {
                // 保存到微信异常表
                wxExceptionDispose(jsonObject, StrUtil.format("获取小程序当前帐号下的模板列表异常"));
                throw new TalkException("微信接口调用失败：" + jsonObject.getString(WeChatConstants.WECHAT_ERR_CODE) + jsonObject.getString(WeChatConstants.WECHAT_ERR_MSG));
            }
        }
        JSONArray templateList = jsonObject.getJSONArray("data");

        return templateList.toJavaList(RoutineTemplateResponse.class);
    }

    /**
     * 删除微信小程序订阅消息
     * @param priTmplId 模版ID
     * @return {@code boolean}
     */
    @Override
    public boolean delRoutineTemplate(String priTmplId) {
        String accessToken = getMiniAccessToken();
        String url = StrUtil.format(WeChatConstants.WECHAT_MINI_DEL_PRIVATE_TEMPLATE_URL, accessToken);
        HashMap<String, String> map = new HashMap<>(2);
        map.put("priTmplId", priTmplId);
        JSONObject jsonData = JSONObject.parseObject(JSONObject.toJSONString(map));
        String result = restTemplateUtil.postJsonData(url, jsonData);
        JSONObject data = JSONObject.parseObject(result);
        if (ObjectUtil.isNull(data)) {
            throw new TalkException("微信平台接口异常，没任何数据返回！");
        }
        if (data.containsKey(WeChatConstants.WECHAT_ERR_CODE) && !data.getString(WeChatConstants.WECHAT_ERR_CODE).equals(WeChatConstants.NUM_ZERO)) {
            if (data.containsKey(WeChatConstants.WECHAT_ERR_MSG)) {
                wxExceptionDispose(data, "删除微信小程序订阅消息异常");
                throw new TalkException("微信接口调用失败：" + data.getString(WeChatConstants.WECHAT_ERR_CODE) + data.getString(WeChatConstants.WECHAT_ERR_MSG));
            }
        }
        return true;
    }

    /**
     * 订阅小程序消息模板
     * @param tempKey 模板编号
     * @return {@code List<RoutineTemplateKeyResponse>}
     */
    @Override
    public List<RoutineTemplateKeyResponse> getRoutineTemplateByWechat(String tempKey) {
        String accessToken = getMiniAccessToken();
        String url = StrUtil.format(WeChatConstants.WECHAT_MINI_GET_TEMPLATE_URL, accessToken, tempKey);
        JSONObject jsonObject = restTemplateUtil.getData(url);
        if (ObjectUtil.isNull(jsonObject)) {
            throw new TalkException("微信平台接口异常，没任何数据返回！");
        }
        if (jsonObject.containsKey(WeChatConstants.WECHAT_ERR_CODE) && !jsonObject.getString(WeChatConstants.WECHAT_ERR_CODE).equals(WeChatConstants.NUM_ZERO)) {
            if (jsonObject.containsKey(WeChatConstants.WECHAT_ERR_MSG)) {
                // 保存到微信异常表
                wxExceptionDispose(jsonObject, "订阅微信小程序消息模版异常");
                throw new TalkException("微信接口调用失败：" + jsonObject.getString(WeChatConstants.WECHAT_ERR_CODE) + jsonObject.getString(WeChatConstants.WECHAT_ERR_MSG));
            }
        }
        JSONArray templateList = jsonObject.getJSONArray("data");

        return templateList.toJavaList(RoutineTemplateKeyResponse.class);
    }

    /**
     * 添加小程序订阅消息
     * @param tempKey 模板编号
     * @param kidList 小程序订阅消息模板kid数组
     * @return {@code String}
     */
    @Override
    public String addRoutineTemplate(String tempKey, List<Integer> kidList) {
        String accessToken = getMiniAccessToken();
        String url = StrUtil.format(WeChatConstants.WECHAT_MINI_API_ADD_TEMPLATE_URL, accessToken);
        HashMap<String, Object> map = new HashMap<>(4);
        map.put("tid", tempKey);
        map.put("kidList", kidList);
        map.put("sceneDesc", "聊来");
        JSONObject jsonData = JSONObject.parseObject(JSONObject.toJSONString(map));
        String result = restTemplateUtil.postJsonData(url, jsonData);
        JSONObject data = JSONObject.parseObject(result);
        if (ObjectUtil.isNull(data)) {
            throw new TalkException("微信平台接口异常，没任何数据返回！");
        }
        if (data.containsKey(WeChatConstants.WECHAT_ERR_CODE) && !data.getString(WeChatConstants.WECHAT_ERR_CODE).equals(WeChatConstants.NUM_ZERO)) {
            if (data.containsKey(WeChatConstants.WECHAT_ERR_MSG)) {
                wxExceptionDispose(data, "添加小程序订阅消息异常");
                throw new TalkException("微信接口调用失败：" + data.getString(WeChatConstants.WECHAT_ERR_CODE) + data.getString(WeChatConstants.WECHAT_ERR_MSG));
            }
        }
        return data.getString("priTmplId");
    }

    /**
     * 微信小程序发送订阅消息
     * @param templateMessage 消息对象
     * @return {@code boolean}
     */
    @Override
    public boolean sendMiniSubscribeMessage(TemplateMessageResponse templateMessage) {
        String accessToken = getMiniAccessToken();
        String url = StrUtil.format(WeChatConstants.WECHAT_MINI_SEND_SUBSCRIBE_URL, accessToken);
        JSONObject messAge = JSONObject.parseObject(JSONObject.toJSONString(templateMessage));
        String result = restTemplateUtil.postJsonData(url, messAge);
        JSONObject data = JSONObject.parseObject(result);
        if (ObjectUtil.isNull(data)) {
            throw new TalkException("微信平台接口异常，没任何数据返回！");
        }
        if (data.containsKey(WeChatConstants.WECHAT_ERR_CODE) && !data.getString(WeChatConstants.WECHAT_ERR_CODE).equals(WeChatConstants.NUM_ZERO)) {
            String error = "40001";
            if (data.getString(WeChatConstants.WECHAT_ERR_CODE).equals(error)) {
                wxExceptionDispose(data, "微信小程序发送订阅消息异常");
                redisUtil.delete(WeChatConstants.REDIS_WECAHT_MINI_ACCESS_TOKEN_KEY);
                accessToken = getMiniAccessToken();
                url = StrUtil.format(WeChatConstants.WECHAT_MINI_SEND_SUBSCRIBE_URL, accessToken);
                result = restTemplateUtil.postJsonData(url, messAge);
                JSONObject data2 = JSONObject.parseObject(result);
                if (data2.containsKey(WeChatConstants.WECHAT_ERR_CODE) && !data2.getString(WeChatConstants.WECHAT_ERR_CODE).equals(WeChatConstants.NUM_ZERO)) {
                    if (data2.containsKey(WeChatConstants.WECHAT_ERR_MSG)) {
                        wxExceptionDispose(data2, "微信小程序发送订阅消息重试异常");
                        throw new TalkException("微信接口调用失败：" + data2.getString(WeChatConstants.WECHAT_ERR_CODE) + data2.getString(WeChatConstants.WECHAT_ERR_MSG));
                    }
                } else {
                    return true;
                }
            }
            if (data.containsKey(WeChatConstants.WECHAT_ERR_MSG)) {
                wxExceptionDispose(data, "微信小程序发送订阅消息异常");
                throw new TalkException("微信接口调用失败：" + data.getString(WeChatConstants.WECHAT_ERR_CODE) + data.getString(WeChatConstants.WECHAT_ERR_MSG));
            }
        }
        return true;
    }

    /**
     * 微信公众号发送模板消息
     * @param templateMessage 模板消息对象
     * @return {@code boolean}
     */
    @Override
    public boolean sendPublicTemplateMessage(TemplateMessageResponse templateMessage) {
        String accessToken = getPublicAccessToken();
        String url = StrUtil.format(WeChatConstants.WECHAT_PUBLIC_SEND_TEMPLATE_URL, accessToken);
        JSONObject jsonData = JSONObject.parseObject(JSONObject.toJSONString(templateMessage));
        String result = restTemplateUtil.postJsonData(url, jsonData);
        JSONObject data = JSONObject.parseObject(result);
        if (ObjectUtil.isNull(data)) {
            throw new TalkException("微信平台接口异常，没任何数据返回！");
        }
        if (data.containsKey(WeChatConstants.WECHAT_ERR_CODE) && !data.getString(WeChatConstants.WECHAT_ERR_CODE).equals(WeChatConstants.NUM_ZERO)) {
            if (data.containsKey(WeChatConstants.WECHAT_ERR_MSG)) {
                wxExceptionDispose(data, "微信公众号发送模板消息异常");
                throw new TalkException("微信接口调用失败：" + data.getString(WeChatConstants.WECHAT_ERR_CODE) + data.getString(WeChatConstants.WECHAT_ERR_MSG));
            }
        }
        return true;
    }

    /**
     * 获取微信公众号自定义菜单配置
     * @return {@code JSONObject}
     */
    @Override
    public JSONObject getPublicCustomMenu() {
        String accessToken = getPublicAccessToken();
        String url = StrUtil.format(WeChatConstants.WECHAT_PUBLIC_MENU_GET_URL, accessToken);
        JSONObject result = restTemplateUtil.getData(url);
        if (ObjectUtil.isNull(result)) {
            throw new TalkException("微信平台接口异常，没任何数据返回！");
        }
        if (result.containsKey(WeChatConstants.WECHAT_ERR_CODE) && result.getString(WeChatConstants.WECHAT_ERR_CODE).equals(WeChatConstants.NUM_ZERO)) {
            return result;
        }
        if (result.containsKey(WeChatConstants.WECHAT_ERR_MSG)) {
            wxExceptionDispose(result, "微信公众号获取自定义菜单配置异常");
            throw new TalkException("微信接口调用失败：" + result.getString(WeChatConstants.WECHAT_ERR_CODE) + result.getString(WeChatConstants.WECHAT_ERR_MSG));
        }
        return result;
    }

    /**
     * 创建微信自定义菜单
     * @param data 菜单json字符串
     * @return {@code boolean}
     */
    @Override
    public boolean createPublicCustomMenu(String data) {
        String accessToken = getPublicAccessToken();
        String url = StrUtil.format(WeChatConstants.WECHAT_PUBLIC_MENU_CREATE_URL, accessToken);
        String result = restTemplateUtil.postJsonData(url, JSONObject.parseObject(data));
        JSONObject jsonObject = JSONObject.parseObject(result);
        if (ObjectUtil.isNull(jsonObject)) {
            throw new TalkException("微信平台接口异常，没任何数据返回！");
        }
        if (jsonObject.containsKey(WeChatConstants.WECHAT_ERR_CODE) && jsonObject.getString(WeChatConstants.WECHAT_ERR_CODE).equals(WeChatConstants.NUM_ZERO)) {
            return true;
        }
        if (jsonObject.containsKey(WeChatConstants.WECHAT_ERR_MSG)) {
            wxExceptionDispose(jsonObject, "微信公众号创建自定义菜单异常");
            throw new TalkException("微信接口调用失败：" + jsonObject.getString(WeChatConstants.WECHAT_ERR_CODE) + jsonObject.getString(WeChatConstants.WECHAT_ERR_MSG));
        }
        return true;
    }

    /**
     * 删除微信自定义菜单
     * @return {@code boolean}
     */
    @Override
    public boolean deletePublicCustomMenu() {
        String accessToken = getPublicAccessToken();
        String url = StrUtil.format(WeChatConstants.WECHAT_PUBLIC_MENU_DELETE_URL, accessToken);
        JSONObject result = restTemplateUtil.getData(url);
        if (ObjectUtil.isNull(result)) {
            throw new TalkException("微信平台接口异常，没任何数据返回！");
        }
        if (result.containsKey(WeChatConstants.WECHAT_ERR_CODE) && result.getString(WeChatConstants.WECHAT_ERR_CODE).equals(WeChatConstants.NUM_ZERO)) {
            return true;
        }
        if (result.containsKey(WeChatConstants.WECHAT_ERR_MSG)) {
            wxExceptionDispose(result, "微信公众号删除自定义菜单异常");
            throw new TalkException("微信接口调用失败：" + result.getString(WeChatConstants.WECHAT_ERR_CODE) + result.getString(WeChatConstants.WECHAT_ERR_MSG));
        }
        return true;
    }

    /**
     * 微信申请退款
     * @param wxRefundVo 微信申请退款对象
     * @param path 商户p12证书绝对路径
     * @return {@code WxRefundResponse}
     */
    @Override
    public WxRefundResponse payRefund(WxRefundVo wxRefundVo, String path) {
        String xmlStr = XmlUtil.objectToXml(wxRefundVo);
        String url = WeChatConstants.PAY_API_URL + WeChatConstants.PAY_REFUND_API_URI_WECHAT;
        HashMap<String, Object> map;
        String xml = "";
        try {
            xml = restTemplateUtil.postWxRefundXml(url, xmlStr, wxRefundVo.getMch_id(), path);
            map = XmlUtil.xmlToMap(xml);
        } catch (Exception e) {
            e.printStackTrace();
            throw new TalkException("xmlToMap错误，xml = " + xml);
        }
        if (null == map) {
            throw new TalkException("微信无信息返回，微信申请退款失败！");
        }

        WxRefundResponse responseVo = TalkUtil.mapToObj(map, WxRefundResponse.class);
        String fail = "FAIL";
        if (responseVo.getReturnCode().toUpperCase().equals(fail)) {
            wxPayExceptionDispose(map, "微信申请退款异常：" + responseVo.getReturnMsg());
            throw new TalkException("微信申请退款失败：" +  responseVo.getReturnMsg());
        }

        if (responseVo.getResultCode().toUpperCase().equals(fail)) {
            wxPayExceptionDispose(map, "微信申请退款业务异常：" + responseVo.getErrCodeDes());
            throw new TalkException("微信申请退款失败：" + responseVo.getErrCodeDes());
        }
        return responseVo;
    }

    /**
     * 获取微信公众号js配置参数
     * @param url 地址
     * @return {@code WeChatJsSdkConfigResponse}
     */
    @Override
    public WeChatJsSdkConfigResponse getJsSdkConfig(String url) {
        try {
            url = URLDecoder.decode(url, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            throw new TalkException("url无法解析！");
        }

        String appId = systemConfigService.getValueByKey(WeChatConstants.WECHAT_PUBLIC_APPID);
        if (StrUtil.isBlank(appId)) {
            throw new TalkException("微信公众号appId未设置");
        }
        String ticket = getJsApiTicket();
        String nonceStr = TalkUtil.getUuid();
        long timestamp = System.currentTimeMillis();
        String signature = getJsSDKSignature(nonceStr, ticket, timestamp , url);

        WeChatJsSdkConfigResponse response = new WeChatJsSdkConfigResponse();
        response.setUrl(url);
        response.setAppId(appId);
        response.setNonceStr(nonceStr);
        response.setTimestamp(timestamp);
        response.setSignature(signature);
        response.setJsApiList(TalkUtil.stringToArrayStr(WeChatConstants.PUBLIC_API_JS_API_SDK_LIST));
        response.setDebug(true);
        return response;
    }

    /**
     * 企业号上传其他类型永久素材
     * 获取url
     * @param type 素材类型:图片（image）、语音（voice）、视频（video），普通文件(file)
     * @return {@code String}
     */
    @Override
    public String qyapiAddMaterialUrl(String type) {
        String accessToken = getPublicAccessToken();
        return StrUtil.format(WeChatConstants.WECHAT_PUBLIC_QYAPI_ADD_MATERIAL_URL, type, accessToken);
    }

    /**
     * 获取公众号accessToken
     * @return String
     */
    private String getPublicAccessToken() {
        boolean exists = redisUtil.exists(WeChatConstants.REDIS_WECAHT_PUBLIC_ACCESS_TOKEN_KEY);
        if (exists) {
            Object accessToken = redisUtil.get(WeChatConstants.REDIS_WECAHT_PUBLIC_ACCESS_TOKEN_KEY);
            return accessToken.toString();
        }
        String appId = systemConfigService.getValueByKey(WeChatConstants.WECHAT_PUBLIC_APPID);
        if (StrUtil.isBlank(appId)) {
            throw new TalkException("微信公众号appId未设置");
        }
        String secret = systemConfigService.getValueByKey(WeChatConstants.WECHAT_PUBLIC_APPSECRET);
        if (StrUtil.isBlank(secret)) {
            throw new TalkException("微信公众号secret未设置");
        }
        WeChatAccessTokenResponse accessToken = getAccessToken(appId, secret, "public");
        // 缓存accessToken
        redisUtil.set(WeChatConstants.REDIS_WECAHT_PUBLIC_ACCESS_TOKEN_KEY, accessToken.getAccessToken(), 88L, TimeUnit.MINUTES);
        return accessToken.getAccessToken();
    }

    /**
     * 获取小程序accessToken
     * @return {@code String}
     */
    private String getMiniAccessToken() {
        boolean exists = redisUtil.exists(WeChatConstants.REDIS_WECAHT_MINI_ACCESS_TOKEN_KEY);
        if (exists) {
            Object accessToken = redisUtil.get(WeChatConstants.REDIS_WECAHT_MINI_ACCESS_TOKEN_KEY);
            return accessToken.toString();
        }
        String appId = systemConfigService.getValueByKey(WeChatConstants.WECHAT_MINI_APPID);
        if (StrUtil.isBlank(appId)) {
            throw new TalkException("微信小程序appId未设置");
        }
        String secret = systemConfigService.getValueByKey(WeChatConstants.WECHAT_MINI_APPSECRET);
        if (StrUtil.isBlank(secret)) {
            throw new TalkException("微信小程序secret未设置");
        }
        WeChatAccessTokenResponse accessToken = getAccessToken(appId, secret, "mini");
        // 缓存accessToken
        redisUtil.set(WeChatConstants.REDIS_WECAHT_MINI_ACCESS_TOKEN_KEY, accessToken.getAccessToken(), 88L, TimeUnit.MINUTES);
        return accessToken.getAccessToken();
    }


    /**
     * 获取微信accessToken
     * @param appId appId
     * @param secret secret
     * @param type mini-小程序，public-公众号，app-开放平台
     * @return {@code WeChatAccessTokenResponse}
     */
    private WeChatAccessTokenResponse getAccessToken(String appId, String secret, String type) {
        String url = StrUtil.format(WeChatConstants.WECHAT_ACCESS_TOKEN_URL, appId, secret);
        JSONObject data = restTemplateUtil.getData(url);
        if (ObjectUtil.isNull(data)) {
            throw new TalkException("微信平台接口异常，没任何数据返回！");
        }
        if (data.containsKey(WeChatConstants.WECHAT_ERR_CODE) && !data.getString(WeChatConstants.WECHAT_ERR_CODE).equals(WeChatConstants.NUM_ZERO)) {
            if (data.containsKey(WeChatConstants.WECHAT_ERR_MSG)) {
                // 保存到微信异常表
                wxExceptionDispose(data, StrUtil.format("微信获取accessToken异常，{}端", type));
                throw new TalkException("微信接口调用失败：" + data.getString(WeChatConstants.WECHAT_ERR_CODE) + data.getString(WeChatConstants.WECHAT_ERR_MSG));
            }
        }
        return JSONObject.parseObject(data.toJSONString(), WeChatAccessTokenResponse.class);
    }

    /**
     * 微信异常处理
     * @param jsonObject 微信返回数据
     * @param remark 备注
     */
    private void wxExceptionDispose(JSONObject jsonObject, String remark) {
        WechatExceptions wechatExceptions = new WechatExceptions();
        wechatExceptions.setErrcode(jsonObject.getString(WeChatConstants.WECHAT_ERR_CODE));
        wechatExceptions.setErrmsg(StrUtil.isNotBlank(jsonObject.getString(WeChatConstants.WECHAT_ERR_MSG)) ? jsonObject.getString(WeChatConstants.WECHAT_ERR_MSG) : "");
        wechatExceptions.setData(jsonObject.toJSONString());
        wechatExceptions.setRemark(remark);
        wechatExceptions.setCreateTime(DateUtil.getNowTime());
        wechatExceptions.setUpdateTime(DateUtil.getNowTime());
        wechatExceptionsService.save(wechatExceptions);
    }

    /**
     * 微信支付异常处理
     * @param map 微信返回数据
     * @param remark 备注
     */
    private void wxPayExceptionDispose(HashMap<String, Object> map, String remark) {
        WechatExceptions wechatExceptions = new WechatExceptions();
        String returnCode = (String) map.get("return_code");
        String fail = "FAIL";
        if (returnCode.toUpperCase().equals(fail)) {
            wechatExceptions.setErrcode("-100");
            wechatExceptions.setErrmsg(map.get("return_msg").toString());
        } else {
            wechatExceptions.setErrcode(map.get("err_code").toString());
            wechatExceptions.setErrmsg(map.get("err_code_des").toString());
        }
        wechatExceptions.setData(JSONObject.toJSONString(map));
        wechatExceptions.setRemark(remark);
        wechatExceptions.setCreateTime(DateUtil.getNowTime());
        wechatExceptions.setUpdateTime(DateUtil.getNowTime());
        wechatExceptionsService.save(wechatExceptions);
    }

    /**
     * 获取JS-SDK的ticket
     * 用于计算签名
     * @return ticket
     */
    private String getJsApiTicket() {
        boolean exists = redisUtil.exists(WeChatConstants.REDIS_PUBLIC_JS_API_TICKET);
        if (exists) {
            Object ticket = redisUtil.get(WeChatConstants.REDIS_PUBLIC_JS_API_TICKET);
            return ticket.toString();
        }
        String accessToken = getPublicAccessToken();
        String url = StrUtil.format(WeChatConstants.WECHAT_PUBLIC_JS_TICKET_URL, accessToken);
        JSONObject data = restTemplateUtil.getData(url);
        if (ObjectUtil.isNull(data)) {
            throw new TalkException("微信平台接口异常，没任何数据返回！");
        }
        if (data.containsKey("errcode") && !data.getString("errcode").equals("0")) {
            if (data.containsKey("errmsg")) {
                // 保存到微信异常表
                wxExceptionDispose(data, "微信获取JS-SDK的ticket异常");
                throw new TalkException("微信接口调用失败：" + data.getString("errcode") + data.getString("errmsg"));
            }
        }
        String ticket = data.getString("ticket");
        redisUtil.set(WeChatConstants.REDIS_PUBLIC_JS_API_TICKET, ticket, WeChatConstants.REDIS_PUBLIC_JS_API_TICKET_EXPRESS, TimeUnit.SECONDS);
        return ticket;
    }

    /**
     * 获取JS-SDK的签名
     * @param nonceStr 随机字符串
     * @param ticket ticket
     * @param timestamp 时间戳
     * @param url url
     * @return 签名
     */
    private String getJsSDKSignature(String nonceStr, String ticket, long timestamp, String url) {
        //注意这里参数名必须全部小写，且必须有序
        String paramString = StrUtil.format("jsapi_ticket={}&noncestr={}&timestamp={}&url={}", ticket, nonceStr, timestamp, url);
        return SecureUtil.sha1(paramString);
    }
}
